package cc.rengu.igas.channel.upqc.core.service.base;

import cc.rengu.igas.channel.upqc.common.constant.UpqcParamConstant;
import cc.rengu.igas.channel.upqc.common.constant.UpqcTreeNodeConstant;
import cc.rengu.igas.channel.upqc.common.util.UpqcSecurityUtil;
import cc.rengu.igas.channel.upqc.core.model.ChnlBizResponse;
import cc.rengu.igas.channel.upqc.core.model.CupsTermInfo;
import cc.rengu.igas.channel.upqc.facade.base.BaseRequest;
import cc.rengu.igas.channel.upqc.facade.bean.RiskInfoBean;
import cc.rengu.oltp.service.base.RadpService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.DstChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.DstTxnAuthCfgMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.DstChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.DstTxnAuthCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.DstChannelInfo;
import cc.rengu.oltp.service.common.entity.DstTxnAuthCfg;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.utility.util.ConvertUtil;
import cc.rengu.oltp.utility.util.RSAUtil;
import cc.rengu.oltp.utility.util.RedisUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.security.interfaces.RSAPrivateKey;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 支付业务处理基类
 */
public abstract class PayService<T> extends RadpService {
    @Override
    protected int bizInit(String s) {
        return 0;
    }

    @Override
    protected int bizProcess(String s, int i) {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        try {
            /* Step1：通道(交易)权限检查 */
            rglog.info("---------------> 通道(交易)权限检查 <---------------");
            channelPermissionCheck();
            /* Step2：业务处理前实现接口 */
            rglog.info("---------------> 业务处理前处理 <---------------");
            T request = beforeBizProcess();
            rglog.error("请求对象request:<{}>", JSON.toJSONString(request));
            /* Step3：公共字段统一处理 */
            convertTermInfo();
            /* Step4：业务逻辑处理 */
            rglog.info("---------------> 业务逻辑处理 <---------------");
            ChnlBizResponse response = callBizService(request);
            /* Step4：业务处理后实现接口 */
            rglog.info("---------------> 业务处理后处理 <---------------");
            afterBizProcess(response);
        } catch (Exception e) {
            String respCode;
            String respDesc;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("业务异常信息:<{}>", byteArrayOutputStream.toString());
            if (e instanceof BizException) {
                respCode = ((BizException) e).getCode();
                respDesc = e.getMessage();
                rglog.error("业务处理失败，业务返回码:<{}>，业务返回信息:<{}>", respCode, respDesc);
            } else {
                rglog.error("系统处理失败，返回信息:<{}>", e);
                respCode = OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespCode();
                respDesc = OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespDesc();
            }
            try {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.RESP_CODE, respCode);
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.RESP_DESC, respDesc);
            } catch (Exception ex) {
                ex.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
                rglog.error("设置节点值异常信息:<{}>", byteArrayOutputStream.toString());
                return -1;
            }

        }
        return 0;
    }

    @Override
    protected int bizTerminate(String s) {
        return 0;
    }

    /**
     * 业务调用前处理
     *
     * @return 业务处理请求结构
     */
    protected abstract T beforeBizProcess() throws Exception;

    /**
     * 业务处理服务调用
     *
     * @param request 业务处理信息
     * @return 业务处理应答结果
     */
    protected abstract ChnlBizResponse callBizService(T request) throws Exception;

    /**
     * 业务调用后处理
     *
     * @param response 业务响应结果
     */
    protected abstract void afterBizProcess(ChnlBizResponse response) throws Exception;

    /**
     * 通道、交易权限权限
     *
     * @throws Exception 业务处理异常
     */
    private void channelPermissionCheck() throws Exception {
        BaseRequest baseRequest = new BaseRequest();
        ConvertUtil.convertOutput(baseRequest);
        rglog.error("请求报文头:<{}>", JSON.toJSONString(baseRequest));
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = baseRequest.getHeader().getInstId();
        String txnNum = baseRequest.getHeader().getTxnNum();
        /* 通道状态检查 */
        rglog.debug("开始支付通道权限检查");
        String dstChannelKey = instId + ChannelEnum.UPQC.getChannelType();
        DstChannelInfo dstChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ISS_INFO_CACHE, dstChannelKey), DstChannelInfo.class);
        if (null == dstChannelInfo) {
            DstChannelInfoMapper dstChannelInfoMapper = new DstChannelInfoMapperImpl();
            dstChannelInfo = dstChannelInfoMapper.selectDstChannelInfoByPrimaryKey(instId, ChannelEnum.UPQC.getChannelType());
        }
        if (null == dstChannelInfo || !dstChannelInfo.getDstChannelStatus().equalsIgnoreCase(AppParamConstant.YES)) {
            rglog.error("支付渠道<{}-{}>未配置或已停用！", ChannelEnum.UPQC.getChannelType(), (null == dstChannelInfo) ? "" : dstChannelInfo.getDstChannelName());
            throw new BizException(OltpRpcdEnum.CHANNEL_DISABLED);
        }
        xmlTreeUtil.setXmlTreeObjectValue(UpqcTreeNodeConstant.DST_CHANNEL_INFO, dstChannelInfo);

        String dstChannelAuthKey = instId.trim() + ChannelEnum.UPQC.getChannelType() + txnNum.trim();
        DstTxnAuthCfg dstTxnAuthCfg = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_TXN_CACHE, dstChannelAuthKey), DstTxnAuthCfg.class);
        if (null == dstTxnAuthCfg) {
            DstTxnAuthCfgMapper dstTxnAuthCfgMapper = new DstTxnAuthCfgMapperImpl();
            dstTxnAuthCfg = dstTxnAuthCfgMapper.selectDstTxnAuthCfgByPrimaryKey(instId, ChannelEnum.UPQC.getChannelType(), txnNum);
        }
        if (null == dstTxnAuthCfg || !dstTxnAuthCfg.getSupportFlag().equalsIgnoreCase(AppParamConstant.YES)) {
            rglog.error("支付通道<{}-{}>不支持此交易，通道交易码:<{}>", dstChannelInfo.getDstChannelId(), dstChannelInfo.getDstChannelName(), txnNum);
            throw new BizException(OltpRpcdEnum.TRANS_NOT_SUPPORT);
        }
        /*获取支付通道交易对应的请求和相应报文存储标志*/
        if (dstTxnAuthCfg.getMsgRegisterFlag() != null && !dstTxnAuthCfg.getMsgRegisterFlag().isEmpty()) {
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.MSG_REG_FLAG, dstTxnAuthCfg.getMsgRegisterFlag());
        }
        rglog.debug("结束支付通道权限检查");
    }

    /**
     * 敏感信息解密处理
     *
     * @param encData 加密数据
     * @throws Exception 异常
     */
    protected String descryptData(String encData) throws Exception {
        rglog.debug("开始敏感信息解密处理");
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        CertInfo encryptCertInfo = null;
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        if (instId == null) {
            instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
        }
        DstChannelInfo dstChannelInfo = (DstChannelInfo) xmlTreeUtil.getXmlTreeObjectValue(UpqcTreeNodeConstant.DST_CHANNEL_INFO, DstChannelInfo.class);
        String encoding = UpqcParamConstant.UTF_8_ENCODING;
        /* 获取敏感信息解密证书 */
        List<SecPlanInfo> secPlanInfoList;
        String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(UpqcTreeNodeConstant.SEC_PLAN_INFO_LIST);
        if (secPlanInfoListString == null) {
            /* 根据通道安全计划获取安全计划 */
            SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
            secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelInfo.getSecPlanId());
            if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
                xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            } else {
                rglog.error("获取通道解密密钥证书失败！");
                return null;
            }
        }
        secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
        Optional<SecPlanInfo> encryptSecPlanOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(UpqcParamConstant.DECRYPT_CERT_TYPE)).findFirst();
        if (encryptSecPlanOpt.isPresent()) {
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            encryptCertInfo = certInfoMapper.selectCertInfoByPrimaryKey(encryptSecPlanOpt.get().getInstId() + encryptSecPlanOpt.get().getSecPlanId() + encryptSecPlanOpt.get().getSecPlanType());
        }
        rglog.debug("结束敏感信息解密处理");
        if ("0".equals(encryptCertInfo.getCertSaveType())) {
            return UpqcSecurityUtil.decryptData(encData, encryptCertInfo.getCertValue(), encoding);
        } else {
            List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(encryptCertInfo.getCertSavePath(), encryptCertInfo.getCertPassword());
            RSAPrivateKey privateKey = RSAUtil.getPrivateKeyFromPfxCertFile(encryptCertInfo.getCertSavePath(), aliasList.get(0), encryptCertInfo.getCertPassword());
            return UpqcSecurityUtil.decryptData(encData, privateKey, encoding);
        }
    }

    /**
     * 敏感信息加密处理
     *
     * @param dataStr 敏感信息
     * @throws Exception 异常
     */
    protected String encData(String dataStr) throws Exception {
        rglog.debug("开始敏感信息加密处理");
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        CertInfo encryptCertInfo = null;
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        if (instId == null) {
            instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
        }
        DstChannelInfo dstChannelInfo = (DstChannelInfo) xmlTreeUtil.getXmlTreeObjectValue(UpqcTreeNodeConstant.DST_CHANNEL_INFO, DstChannelInfo.class);
        /* 获取敏感信息解密证书 */
        List<SecPlanInfo> secPlanInfoList;
        String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(UpqcTreeNodeConstant.SEC_PLAN_INFO_LIST);
        if (secPlanInfoListString == null) {
            /* 根据通道安全计划获取安全计划 */
            SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
            secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelInfo.getSecPlanId());
            if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
                xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            } else {
                rglog.error("获取通道加密密钥证书失败！");
                return null;
            }
        } else {
            secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
        }

        /* 获取敏感信息加密证书 */
        Optional<SecPlanInfo> encryptSecPlanOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(UpqcParamConstant.ENCRYPT_CERT_TYPE)).findFirst();
        if (encryptSecPlanOpt.isPresent()) {
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            encryptCertInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + encryptSecPlanOpt.get().getSecPlanId() + encryptSecPlanOpt.get().getSecPlanType());
        }
        rglog.debug("结束敏感信息加密处理");
        if ("2".equals(encryptCertInfo.getCertSaveType())) {
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.encryptCertId, UpqcSecurityUtil.getPubCertIdFromCertFile(encryptCertInfo.getCertSavePath()));
            return UpqcSecurityUtil.encryptData(dataStr, RSAUtil.getPublicKeyFromCertFile(encryptCertInfo.getCertSavePath()), UpqcParamConstant.UTF_8_ENCODING);
        } else {
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.encryptCertId, encryptCertInfo.getCertId());
            return UpqcSecurityUtil.encryptData(dataStr, encryptCertInfo.getCertValue(), UpqcParamConstant.UTF_8_ENCODING);
        }

    }

    protected T convertKvStr2Bean(String kvStr, Class<T> beanClass) throws Exception {
        Map<String, Object> kvMap = UpqcSecurityUtil.parseKvString(kvStr);
        JSONObject jsonObject = new JSONObject(kvMap);
        return JSON.parseObject(jsonObject.toJSONString(), beanClass);
    }

    private void convertTermInfo() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        CupsTermInfo cupsTermInfo = new CupsTermInfo();
        ConvertUtil.convertOutput(cupsTermInfo);
        if (cupsTermInfo.getTermInfo() != null && isNotEmpty(cupsTermInfo.getTermInfo())) {
            Map<String, String> map = JSON.parseObject(JSON.toJSONString(cupsTermInfo.getTermInfo()), Map.class);
            rglog.info("termInfo=[{}]", UpqcSecurityUtil.covertResultMap2String(map));
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.termInfoStr, Base64.encodeBase64String(UpqcSecurityUtil.covertMap2KVString(map).getBytes()));
        }
    }

    protected void convertRiskInfo(RiskInfoBean riskInfoBean) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (isNotEmpty(riskInfoBean)) {
            Map<String, String> map = JSON.parseObject(JSON.toJSONString(riskInfoBean), Map.class);
            rglog.info("RiskInfo=[{}]", UpqcSecurityUtil.covertResultMap2String(map));
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.riskInfoStr, Base64.encodeBase64String(UpqcSecurityUtil.covertMap2KVString(map).getBytes()));
        }
    }

    /**
     * 判断对象是否为空
     *
     * @param object 对象
     * @return true-不为空,false-为空
     */
    protected static boolean isNotEmpty(Object object) {
        if (null == object) {
            return false;
        } else {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    String fieldTypeName = field.getType().getSimpleName();
                    if ("List".equals(fieldTypeName)) {
                        List<Object> objectList = (List<Object>) object;
                        return isNotEmpty(objectList.get(0));
                    } else {
                        if (null != field.get(object)) {
                            return true;
                        }
                    }
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }
}
